package com.example.gulimall.product.web;

import com.example.common.constant.RedisConstant;
import com.example.gulimall.product.entity.CategoryEntity;
import com.example.gulimall.product.service.CategoryService;
import com.example.gulimall.product.vo.Catelog2Vo;
import lombok.SneakyThrows;
import org.redisson.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 首页信息
 *
 * @author GuLin
 * @date 2021-06-16 9:26
 */
@Controller
public class IndexController {

    @Autowired
    CategoryService categoryService;

    @Autowired
    RedissonClient redisson;
    @Autowired
    StringRedisTemplate redisTemplate;


    /**
     * 首页访问
     * 由于模板引擎
     *       前缀为 classpath:/templates/
     *      后缀为 .html
     * 所以我们只需要返回具体html页面的字符即可
     *
     * @return java.lang.String
     * @author GuLin
     * @date 9:28 2021/6/16
     **/
    @GetMapping({"/","index.html"})
    public String getIndex(Model model) {
        //System.out.println("访问首页===>"+Thread.currentThread().getId());
        //查询所有的一级分类信息
        List<CategoryEntity> categoryEntityList = categoryService.getLevel1Catagories();
        model.addAttribute("catagories", categoryEntityList);
        return "index";
    }

    /**
     * 渲染子菜单
     * @author GuLin
     * @date 10:03 2021/6/16
     * @return Map<String, List < Catelog2Vo>>
     **/
    @ResponseBody
    @RequestMapping("index/catalog.json")
    public Map<String, List<Catelog2Vo>> getCatlogJson() {

        Map<String, List<Catelog2Vo>> map = categoryService.getCatelogJsonFroSpringCache();
                //categoryService.getCatelogJson();
        return map;
    }

    /**
     * 可重入锁
     * @return
     */
    @ResponseBody
    @RequestMapping("hello")
    public String hello(){
        //可重入锁,获取一把锁，只要名字一样就是同一把锁
        RLock lock = redisson.getLock(RedisConstant.ANYLOCK);
        //2、加锁
        lock.lock();      //阻塞式等待。默认加的锁都是30s
        // 加锁以后10秒钟自动解锁,无需调用unlock方法手动解锁,否则会报错IllegalMonitorStateException
        //自己指定的时间一定要大于业务（看门狗）设定的时间
        //只要占锁成功，就会启动一个定时任务【重新给锁设置过期时间，新的过期时间就是看门狗的默认时间】,每隔10秒都会自动的再次续期，续成30秒
        //lock.lock(10, TimeUnit.SECONDS); //推荐使用此方法
        System.out.println("加锁成功，执行业务..." + Thread.currentThread().getId());
        try {
            TimeUnit.SECONDS.sleep(30);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            System.out.println("释放锁..." + Thread.currentThread().getId());
            lock.unlock(); // 如果这里宕机：有看门狗，不用担心,30s之后自动解锁
        }
        return "hello";
    }

    /**
     * 分布式可重入读写锁允许同时有多个读锁和一个写锁处于加锁状态。
     * 读锁是一个共享锁，必须等写锁释放才能读取数据
     * @return
     */
    @ResponseBody
    @RequestMapping("/readLock")
    public String readLock(){
        RReadWriteLock rwlock = redisson.getReadWriteLock(RedisConstant.ANYRMLOCK);
        RLock rLock = rwlock.readLock();
        String uuid = null;
        try {
            rLock.lock();
            TimeUnit.SECONDS.sleep(30);
            uuid = redisTemplate.opsForValue().get(RedisConstant.UUID);
        }catch (InterruptedException e){
            e.printStackTrace();
        } finally {
            rLock.unlock();
        }
        return uuid;
    }

    /**
     * 分布式可重入读写锁允许同时有多个读锁和一个写锁处于加锁状态。
     * 修改期间写锁是一个排他锁（互斥锁）
     * 写锁没有释放，读锁就必须等待
     * 读 + 读 ：相当于无锁，并发读，只会在Redis中记录好，所有当前的读锁。他们都会同时加锁成功
     * 写 + 读 ：必须等待写锁释放
     * 写 + 写 ：阻塞方式
     * 读 + 写 ：有读锁。写也需要等待
     * 只要有读或者写的存都必须等待
     * @return
     */
    @ResponseBody
    @RequestMapping("/writeLock")
    public String writeLock(){
        RReadWriteLock rwlock = redisson.getReadWriteLock(RedisConstant.ANYRMLOCK);
        RLock rLock = rwlock.writeLock();
        String uuid = UUID.randomUUID().toString();
        try {
            rLock.lock();
            TimeUnit.SECONDS.sleep(30);
            redisTemplate.opsForValue().set(RedisConstant.UUID,uuid);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            rLock.unlock();
        }
       return uuid;
    }

    /**
     * 车库停车
     * 信号量、可做分布式限流
     * @return
     */
    @ResponseBody
    @GetMapping("/park")
    public String park() throws InterruptedException {
        RSemaphore semaphore = redisson.getSemaphore(RedisConstant.SEMAPHORE);
        semaphore.acquire(); //获取一个信号，获取一个值信息，阻塞式等待
        boolean b = semaphore.tryAcquire();//返回true/false 非阻塞式等待，如果没有返回false
        return "ok==>"+b;
    }

    @ResponseBody
    @GetMapping("/go")
    public String go() throws InterruptedException {
        RSemaphore semaphore = redisson.getSemaphore(RedisConstant.SEMAPHORE);
        semaphore.release(); //释放一个信号
        return "ok";
    }

    /**
     * 放假、锁门
     * 1班没人了
     * 5个班，全部走完，我们才可以锁大门
     * 分布式闭锁
     */
    @SneakyThrows
    @ResponseBody
    @RequestMapping("/lockDoor")
    public String lockDoor() throws InterruptedException {
        RCountDownLatch latch = redisson.getCountDownLatch(RedisConstant.ANYCOUNTDOWNLATCH);
        latch.trySetCount(5); //设置任务数
        latch.await(); //等候
        return "放假了....";
    }
    @ResponseBody
    @GetMapping("/gogogo/{id}")
    public String lockDoor(@PathVariable("id") Long id) throws InterruptedException {
        RCountDownLatch latch = redisson.getCountDownLatch(RedisConstant.ANYCOUNTDOWNLATCH);
        latch.countDown(); //计数减一
        return id+"班的人走了....";
    }


}
